package com.common.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.IColumnType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.text.WordUtils;

import java.io.*;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 代码生成器工具类
 * </p>
 *
 * @author Caratacus
 */
@Slf4j
public class CodeAutoGeneratorUtil {

    private static String[] superEntityColumns = new String[]{"id", "searchKeys", "createDate", "updateDate", "delFlag", "createUserId", "updateUserId", "memo"};

    /**
     * 获取AutoGenerator
     *
     * @param conf
     * @return
     */
    public static void codeAutoGenerator(Dict conf, DataSourceProperty dataSourceProperty) {
        String tableName = conf.getStr("tableName");
        String moduleName = conf.getStr("moduleName");
        String author = conf.getStr("author");
        String layout = conf.getStr("layout");
        Boolean isFileOverride = conf.getBool("isFileOverride");
        String browseRootPath = conf.getStr("browsePath");
        String javaRootPath = conf.getStr("javaPath");

        List<String> genItems = (List<String>) conf.get("genItem");
        if(CollUtil.isNotEmpty(genItems)) {
            updateRouterConfigJS(browseRootPath, moduleName, tableName);
        }

        AutoGenerator mpg = new AutoGenerator();
        // 全局配置
        mpg.setGlobalConfig(getGlobalConfig(author, isFileOverride, javaRootPath));
        // 数据源配置
        mpg.setDataSource(getDataSourceConfig(dataSourceProperty));
        // 策略配置
        mpg.setStrategy(getStrategyConfig(tableName, layout));
        // 包配置
        mpg.setPackageInfo(getPackageConfig(moduleName));
        // 注入自定义配置，可以在 VM 中使用 cfg.abc 设置的值
        mpg.setCfg(getInjectionConfig(conf, moduleName, browseRootPath, javaRootPath));
        mpg.setTemplate(getTemplateConfig());
        mpg.execute();
    }

    /**
     * 获取TemplateConfig
     *
     * @return
     */
    public static TemplateConfig getTemplateConfig() {
        return new TemplateConfig().setXml(null);
    }

    /**
     * 获取InjectionConfig
     *
     * @return
     */
    public static InjectionConfig getInjectionConfig(Map<String, Object> param, String moduleName, String browseRootPath, String javaRootPath) {

        // 注入自定义配置，可以在 VM 中使用 cfg.abc 【可无】
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                this.setMap(param);
            }
        };

        // 自定义 模板 生成
        List<FileOutConfig> focList = CollUtil.newArrayList();
        focList.add(new FileOutConfig("/templates/mapper.xml.vm") {
            // 自定义输出文件目录
            @Override
            public String outputFile(TableInfo tableInfo) {
                return getResourcePath(javaRootPath) + File.separator + "mappings" + File.separator + moduleName + File.separator + tableInfo.getEntityName() + "Mapper.xml";
            }
        });

        List<String> genItems = (List<String>) param.get("genItem");
        if(CollUtil.isNotEmpty(genItems)) {
            focList.add(new FileOutConfig("/templates/models.js.vm") {
                // 自定义输出文件目录
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return browseRootPath + File.separator + "src" + File.separator + "models" + File.separator + moduleName + File.separator + (tableInfo.getEntityName().substring(0,1).toLowerCase()+tableInfo.getEntityName().substring(1,tableInfo.getEntityName().length())) + ".js";
                }
            });

            focList.add(new FileOutConfig("/templates/pages.js.vm") {
                // 自定义输出文件目录
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return browseRootPath + File.separator + "src" + File.separator + "pages" + File.separator + (moduleName.substring(0,1).toUpperCase()+moduleName.substring(1,moduleName.length())) + File.separator + (tableInfo.getEntityName()) + ".js";
                }
            });

            focList.add(new FileOutConfig("/templates/services.js.vm") {
                // 自定义输出文件目录
                @Override
                public String outputFile(TableInfo tableInfo) {
                    String path = browseRootPath + File.separator + "src" + File.separator + "services" + File.separator + (tableInfo.getEntityName().substring(0,1).toLowerCase()+tableInfo.getEntityName().substring(1,tableInfo.getEntityName().length())) + ".js";
                    if(FileUtil.exist(path)) {
                        FileUtil.del(FileUtil.file(path));
                    }
                    return path;
                }
            });
        }

        cfg.setFileOutConfigList(focList);
        return cfg;
    }

    /**
     * 获取PackageConfig
     *
     * @return
     */
    public static PackageConfig getPackageConfig(String moduleName) {
        return new PackageConfig()
                .setModuleName(moduleName)
                .setParent("com.modules")
                .setController("controller")
                .setEntity("entity")
                .setMapper("mapper")
                .setService("service")
                .setServiceImpl("service.impl");
    }

    /**
     * 获取StrategyConfig
     *
     * @param tableName
     * @param layout
     * @return
     */
    public static StrategyConfig getStrategyConfig(String tableName, String layout) {
        return new StrategyConfig()
                // 全局大写命名
                .setCapitalMode(false)
                // 表名生成策略
                .setNaming(NamingStrategy.underline_to_camel)
                //自定义实体父类
                .setSuperEntityClass(StrUtil.equals(layout, "treeTable") ? "com.common.entity.TreeNode" : "com.common.entity.DataEntity")
                // 自定义实体，公共字段
                .setSuperEntityColumns(superEntityColumns)
                // 自定义 mapper 父类
                .setSuperMapperClass("com.common.mapper.BaseMapper")
                // 自定义 controller 父类
                .setSuperControllerClass("com.common.web.BaseController")
                // 自定义 service 实现类父类
                .setSuperServiceImplClass("com.common.service.BaseServiceImpl")
                // 自定义 service 接口父类
                .setSuperServiceClass("com.common.service.BaseService")
                // 【实体】是否生成字段常量（默认 false）
                .setEntityColumnConstant(true)
                // 【实体】是否为构建者模型（默认 false）
                .setEntityBuilderModel(false)
                // 【实体】是否为lombok模型（默认 false）<a href="https://projectlombok.org/">document</a>
                .setEntityLombokModel(true)
                .setRestControllerStyle(true)
                .setInclude(tableName);
    }

    /**
     * 获取DataSourceConfig
     *
     * @return
     */
    public static DataSourceConfig getDataSourceConfig(DataSourceProperty dataSourceProperty) {
        return new DataSourceConfig()
                // 数据库类型
                .setDbType(DbType.MYSQL)
                .setTypeConvert(new MySqlTypeConvert() {
                    @Override
                    public IColumnType processTypeConvert(GlobalConfig globalConfig, String fieldType) {
                        if (fieldType.toLowerCase().contains("bit")) {
                            return DbColumnType.BOOLEAN;
                        }
                        if (fieldType.toLowerCase().contains("int")) {
                            return DbColumnType.LONG;
                        }
                        if (fieldType.toLowerCase().contains("tinyint")) {
                            return DbColumnType.BOOLEAN;
                        }
                        if (fieldType.toLowerCase().contains("date")) {
                            return DbColumnType.DATE;
                        }
                        if (fieldType.toLowerCase().contains("time")) {
                            return DbColumnType.DATE;
                        }
                        if (fieldType.toLowerCase().contains("datetime")) {
                            return DbColumnType.DATE;
                        }
                        return super.processTypeConvert(globalConfig, fieldType);
                    }
                })
                .setDriverName(dataSourceProperty.getDriverClassName())
                .setUsername(dataSourceProperty.getUsername())
                .setPassword(dataSourceProperty.getPassword())
                .setUrl(dataSourceProperty.getUrl());
    }

    /**
     * 获取GlobalConfig
     *
     * @return
     */
    public static GlobalConfig getGlobalConfig(String author, Boolean isFileOverride, String javaRootPath) {
        return new GlobalConfig()
                //输出目录
                .setOutputDir(getJavaPath(javaRootPath))
                // 是否覆盖文件
                .setFileOverride(isFileOverride)
                // 开启 activeRecord 模式
                .setActiveRecord(false)
                // XML 二级缓存
                .setEnableCache(false)
                // XML ResultMap
                .setBaseResultMap(false)
                // XML columList
                .setBaseColumnList(false)
                //是否生成 kotlin 代码
                .setKotlin(false)
                .setOpen(false)
                //作者
                .setAuthor(author)
                .setServiceName("%sService");
    }


    /**
     * 获取JAVA目录
     *
     * @return
     */
    public static String getRouterConfigPath(String browseRootPath) {
        return browseRootPath + "/config/router.config.js";
    }

    /**
     * 获取JAVA目录
     *
     * @return
     */
    public static String getJavaPath(String javaRootPath) {
        return javaRootPath + "/src/main/java";
    }

    /**
     * 获取Resource目录
     *
     * @return
     */
    public static String getResourcePath(String javaRootPath) {
        return javaRootPath + "/src/main/resources";
    }

    /**
     * 修改js文件
     * @param browseRootPath
     * @param moduleName
     * @param tableName
     */
    public static void updateRouterConfigJS(String browseRootPath, String moduleName, String tableName){
        try {
            //第一步先读入js中所有的内容放到String中
            File file = new File(getRouterConfigPath(browseRootPath));
            InputStream stream = new FileInputStream(file);
            InputStreamReader isreader = new InputStreamReader(stream);
            // new FileReader(new File(file))
            BufferedReader reader=new BufferedReader(isreader);
            StringBuilder sb=new StringBuilder();
            String line=null;
            while((line=reader.readLine())!=null){
                //把换行符去掉
                sb.append(line);
            }
            reader.close();
            String sub=sb.substring(sb.indexOf("["), sb.lastIndexOf("]")+1);
            JSONArray jo = null;
            if(JSONUtil.isJsonArray(sub)) {
                jo = JSONUtil.parseArray(sub);
            }

            // 可直接将这个jo输出到文件中
            List<JSONObject> jsonObjectList = jo.toList(JSONObject.class);
            jsonObjectList.forEach((JSONObject routeObj) -> {
                if(StrUtil.equals(routeObj.getStr("path"), "/")) {
                    JSONArray routesArr = routeObj.getJSONArray("routes");
                    List<JSONObject> routesList = routesArr.toList(JSONObject.class);
                    boolean isExist = false;

                    String daxie = StrUtil.sub(moduleName, 0, 1).toUpperCase()+StrUtil.sub(moduleName, 1, moduleName.length());
                    String subPath = StrUtil.format("/{}/{}", moduleName, columnToJava(tableName));
                    String subComponent = StrUtil.format("./{}/{}", daxie, columnToJava(tableName));

                    for (JSONObject jsonObject : routesList) {
                        if(StrUtil.equals(jsonObject.getStr("path"), "/"+moduleName)) {

                            List<JSONObject> routesSubList = jsonObject.getJSONArray("routes").toList(JSONObject.class);

                            for (JSONObject jsonObject1 : routesSubList) {
                                if(StrUtil.equals(jsonObject1.getStr("path"), subPath)) {
                                    jsonObject.getJSONArray("routes").remove(jsonObject1);
                                }
                            }

                            isExist = true;
                            JSONObject routesNew = JSONUtil.createObj();
                            routesNew.put("path", subPath);
                            routesNew.put("component", subComponent);
                            jsonObject.getJSONArray("routes").add(routesNew);
                            break;
                        }
                    }

                    if(!isExist) {

                        JSONObject routesNew = JSONUtil.createObj();
                        routesNew.put("path", subPath);
                        routesNew.put("component", subComponent);

                        JSONArray routesArrNew = JSONUtil.createArray();
                        routesArrNew.add(routesNew);

                        JSONObject routesParentNew = JSONUtil.createObj();
                        routesParentNew.put("path", StrUtil.format("/{}", moduleName));
                        routesParentNew.put("routes", routesArrNew);
                        routesArr.add(routesParentNew);
                    }
                }
            });

            //组装文件内容
            StringBuilder builder = new StringBuilder();
            builder.append("export default ");
            builder.append(JsonFormatTool.formatJson(jo.toString()));
            builder.append(";");
            //将内容输出到文件
            FileWriter writer=new FileWriter(file);
            writer.write(builder.toString());
            writer.flush();
            writer.close();
        }catch (Exception e){
            log.error("修改router.config.js文件失败", e);
        }
    }

    /**
     * 列名转换成Java属性名
     */
    public static String columnToJava(String columnName) {
        return WordUtils.capitalizeFully(columnName, new char[]{'_'}).replace("_", "");
    }


    /**
     * 根据驼峰命名，首字母大写
     * @param tabName 原名
     * @return 返回生成后的名字
     *  例如：user_info 返回 UserInfo
     */
    public static String getName(String tabName, String reChar) {
        String[] arr = tabName.split(reChar);
        String str = "";
        for (int i = 0; i < arr.length; i++ ) {
            String startChar = arr[i].substring(0,1).toUpperCase();
            String lastChar = arr[i].substring(1, arr[i].length());
            String newStr = startChar + lastChar;
            str += newStr;
        }
        return str;
    }
}

